import { CheckData } from "./CheckData.js";
import type {
    CustomResField,
    EqualResField,
    HexResField,
    NumberResField,
    ResponseOption,
    StringResField,
} from "./ResponseOption.js";

type Result = string | null;

export class Parser {
    readonly option: ResponseOption;

    constructor(option: ResponseOption) {
        this.option = option;
    }

    #custom(checkData: CheckData, field: CustomResField): Result {
        const result = field.matcher(checkData);
        if (result === null) return null;
        return `${field.name}: ${result}`;
    }

    #equal(checkData: CheckData, field: EqualResField): Result {
        if (!checkData.hasPrefix(field.data)) return null;
        checkData.consume(field.data.length);
        return `${field.name}: ${field.value}`;
    }

    #hex(checkData: CheckData, field: HexResField): Result {
        const len = field.length ?? checkData.length;
        const str = checkData.hex(len);
        checkData.consume(len);
        return `${field.name}: ${str}`;
    }

    #u8(checkData: CheckData, field: NumberResField): Result {
        if (checkData.length < 1) return null;
        const value = checkData.u8(0);
        checkData.consume(1);
        return `${field.name}: ${value}`;
    }

    #u16(checkData: CheckData, field: NumberResField): Result {
        if (checkData.length < 2) return null;
        const value = checkData.u16le(0);
        checkData.consume(2);
        return `${field.name}: ${value}`;
    }

    #u32(checkData: CheckData, field: NumberResField): Result {
        if (checkData.length < 4) return null;
        const value = checkData.u32le(0);
        checkData.consume(4);
        return `${field.name}: ${value}`;
    }

    #string(checkData: CheckData, field: StringResField): Result {
        const len = field.length ?? checkData.length;
        const str = checkData.string(len);
        checkData.consume(len);
        return `${field.name}: ${str}`;
    }

    check(buf: Uint8Array): string | null {
        const checkData = new CheckData(buf);

        let part: Result = null;
        let result = "";

        for (const field of this.option.fields) {
            switch (field.kind) {
                case "custom":
                    part = this.#custom(checkData, field);
                    break;
                case "equal":
                    part = this.#equal(checkData, field);
                    break;
                case "hex":
                    part = this.#hex(checkData, field);
                    break;
                case "string":
                    part = this.#string(checkData, field);
                    break;
                case "u8":
                    part = this.#u8(checkData, field);
                    break;
                case "u16":
                    part = this.#u16(checkData, field);
                    break;
                case "u32":
                    part = this.#u32(checkData, field);
                    break;
                default:
                    throw new Error(
                        `不支持的规则类型, res: ${this.option.name}, name: ${field.name}, rule: ${field.kind}`
                    );
            }
            if (part === null) {
                // console.error(`数据不符合规则,res: ${this.option.name}, name: ${field.name}, rule: ${field.kind}`);
                return null;
            } else {
                result += part + "\n";
            }
        }
        if (checkData.length !== 0) {
            console.error("数据长度不匹配", checkData.length, checkData.consume(checkData.length));
            return null;
        }
        return result;
    }
}
